/*    
 * Copyright (c) 2013 ISoftStone, Inc.
 * All right reserved.
 *
 * 文件名：      PayServiceImpl.java
 * 作者:     Jacky Wang
 * 创建日期： 2013-8-6 上午10:04:57
 * 版本：           
 *
 */
package com.isoftstone.industry.aaa.core.service.accounting.impl;

import java.io.File;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.List;

import javax.annotation.Resource;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.isoftstone.industry.common.util.SNGenerator;
import com.isoftstone.industry.persistance.bean.Account;
import com.isoftstone.industry.persistance.bean.AccountBook;
import com.isoftstone.industry.persistance.bean.AccountItem;
import com.isoftstone.industry.persistance.bean.AccountItemType;
import com.isoftstone.industry.persistance.bean.Bill;
import com.isoftstone.industry.persistance.bean.BillItem;
import com.isoftstone.industry.persistance.bean.BillingList;
import com.isoftstone.industry.persistance.bean.Order;
import com.isoftstone.industry.persistance.bean.PayReceipt;
import com.isoftstone.industry.persistance.bean.PayReceiptDetail;
import com.isoftstone.industry.persistance.bean.PricingInfoSnapshot;
import com.isoftstone.industry.persistance.bean.Product;
import com.isoftstone.industry.persistance.bean.ProductInstance;
import com.isoftstone.industry.persistance.bean.ReceiptDisburseStatement;
import com.isoftstone.industry.persistance.bean.SpAcctBookItemReceipt;
import com.isoftstone.industry.persistance.bean.SpecialAccountBookItem;
import com.isoftstone.industry.persistance.bean.User;
import com.isoftstone.industry.aaa.core.log.BusinessLogger;
import com.isoftstone.industry.aaa.core.log.ILogMessages;
import com.isoftstone.industry.aaa.core.service.accounting.IChargeOffService;
import com.isoftstone.industry.aaa.core.service.accounting.IPayService;
import com.isoftstone.industry.aaa.core.service.assist.IAccountBookService;
import com.isoftstone.industry.aaa.core.service.assist.IAccountItemService;
import com.isoftstone.industry.aaa.core.service.assist.IAccountService;
import com.isoftstone.industry.aaa.core.service.assist.IBillItemService;
import com.isoftstone.industry.aaa.core.service.assist.IBillService;
import com.isoftstone.industry.aaa.core.service.assist.IBillingListService;
import com.isoftstone.industry.aaa.core.service.assist.IOrderService;
import com.isoftstone.industry.aaa.core.service.assist.IPayReceiptService;
import com.isoftstone.industry.aaa.core.service.assist.IProductInstService;
import com.isoftstone.industry.aaa.core.service.assist.IReceiptDisburseStatementService;
import com.isoftstone.industry.aaa.core.service.assist.ISpAcctBookItemReceiptService;
import com.isoftstone.industry.aaa.core.service.assist.ISpecialAccountBookItemService;
import com.isoftstone.industry.aaa.core.service.assist.IUserService;
import com.isoftstone.industry.aaa.exception.AAAException;
import com.isoftstone.industry.aaa.exception.BalanceNotEnoughException;
import com.isoftstone.industry.aaa.exception.IAAAErrorCode;
import com.isoftstone.industry.aaa.utils.SystemProperties;
import com.isoftstone.modules.core.utils.DateUtils;
import com.isoftstone.modules.core.utils.lock.FileProgrameLock;
import com.isoftstone.modules.core.utils.lock.Lock;

/**
 * @author jacky
 */
@Service
public class PayServiceImpl implements IPayService {
	@Resource
	private BusinessLogger logger;

	@Resource
	private IAccountItemService acctItemService;

	@Resource
	private ISpecialAccountBookItemService spAcctBookItemService;

	@Resource
	private IAccountBookService acctBookService;

	@Resource
	private IAccountService accountService;

	@Resource
	private IBillingListService billingListService;

	@Resource
	private IChargeOffService chargeOffService;

	@Resource
	private IProductInstService prodInstService;

	@Resource
	private IUserService userService;

	@Resource
	private IBillService billService;

	@Resource
	private IBillItemService billItemService;

	@Resource
	private IOrderService orderService;

	@Resource
	private IPayReceiptService payReceiptService;

	@Resource
	private SystemProperties systemProperties;
	
	@Resource
	private ISpAcctBookItemReceiptService spAcctBookItemReceiptService;

	@Resource
	private IReceiptDisburseStatementService receiptDisburseStatementService;
	
	private SpecialAccountBookItem saveOrUpdateSpItem(AccountBook acctBook,
			String source, String sourceId, String type, double balance, String sourceName) {
		SpecialAccountBookItem persist = null;
		if (SpecialAccountBookItem.Type.GIVEN.equals(type)) {
			persist = spAcctBookItemService.findGevinInstItem(acctBook.getId(),
					sourceId, source);
		} else if (SpecialAccountBookItem.Type.PREPAID.equals(type)) {
			persist = spAcctBookItemService.findPrepaidInstItem(acctBook
					.getId(), sourceId, source);
		} else {
			throw new RuntimeException("无效的专款专用类型:" + type);
		}
		Timestamp now = DateUtils.getCurrentTimestamp();
		if (persist == null) {
			SpecialAccountBookItem spItem = new SpecialAccountBookItem();
			spItem.setAccountBook(acctBook);
			spItem.setBalance(balance);
			spItem.setSn(SNGenerator.createSpAcctItemSn());
			spItem.setName(spItem.getSn());
			spItem.setCreatedTime(now);
			spItem.setModifiedTime(now);
			spItem.setSource(source);
			spItem.setSourceName(sourceName);
			spItem.setType(type);
			spItem.setSourceId(sourceId);
			spItem.setFreezeBalance(0.00);
			spItem.setEffDate(DateUtils.today(DateUtils.YYYY_MM_DD_PATTERN));
			spItem.setExpDate(DateUtils
					.getMaxDate(DateUtils.YYYY_MM_DD_PATTERN));
			persist = spAcctBookItemService.save(spItem);
			logger.info(ILogMessages.SAVE_SPACCTBOOKITEM, persist.getSn());
		} else {
			persist.setBalance(persist.getBalance() + balance);
			persist.setModifiedTime(now);
			persist = spAcctBookItemService.update(persist);
			logger.info(ILogMessages.UPDATE_SPACCTBOOKITEM, persist.getSn());
		}
		return persist;
	}

	@Transactional
	@Override
	public void timingPay4PrepaidBillingList() {
		// 找出需要扣费的计费清单（计费清单类型=预付费一次性||预付费周期性）
		// 为了防止计费清单过大，进行分页处理
		long total = billingListService.countNeedPrepaidItems();
		int limit = 50;
		int start = 0;
		int pageNum = 0;
		while (start < total) {
			List<BillingList> page = billingListService.findNeedPrepaidItems(
					start, limit);
			for (BillingList billingList : page) {
				Lock lock = getFileLock(billingList);
				// 若可以成功获取文件锁，则代表没有进程正在为该用户进行出帐
				if (lock.obtain() == true) {
					try {
						pre4PrepaidBillList(billingList);
					}catch (Exception e){
						logger.error(e.getMessage(), e);
					}finally{
						// 释放文件锁
						lock.unlock();
					}
				}
			}
			pageNum++;
			start += pageNum * limit;
		}
	}

	@Transactional(propagation = Propagation.REQUIRES_NEW)
	public void pre4PrepaidBillList(BillingList billingList) {
		AccountItemType acctItemType = billingList.getAcctItemType();
		ProductInstance prodInst = billingList.getProductInstance();
		PricingInfoSnapshot pricingInfoSnapshot = billingList
				.getChargingItemDetail().getPricingInfoSnapshot();
		AccountItem acctItem = acctItemService.createAccountItem(acctItemType,
				prodInst, billingList.getAmount(), pricingInfoSnapshot, null);
		acctItem = chargeOffService.chargeOff4AcctItem(acctItem.getId());
		billingListService.update4AcctItem(
				new String[] { billingList.getId() }, acctItem);
		logger.info(ILogMessages.PREPAY_BILLING_LIST, billingList.getSn(),
				billingList.getAmount(), acctItem.getPaidAmount());
		// 如果该帐目有欠费，则停用其产品实例
		if (acctItem.getPayableAmount() > 0) {
			prodInstService.disable(prodInst.getId());
			logger.info(ILogMessages.DISABLE_PRODINST_4_OWE_FEE, billingList
					.getSn(), billingList.getAmount(),
					acctItem.getPaidAmount(), prodInst.getSn());
		}
	}

	private Lock getFileLock(BillingList billingList) {
		String fileName = systemProperties.getFileLockDir() + File.separator
				+ "paying4billinglist_" + billingList.getId() + ".lock";
		return FileProgrameLock.get(fileName);
	}

	private Lock getFileLock(User user) {
		String fileName = systemProperties.getFileLockDir() + File.separator
				+ "pay4ownfeeuser_" + user.getId() + ".lock";
		return FileProgrameLock.get(fileName);
	}

	private Lock getFileLock(String orderId) {
		String fileName = systemProperties.getFileLockDir() + File.separator
				+ "pay4order_" + orderId + ".lock";
		return FileProgrameLock.get(fileName);
	}

	@Override
	@Transactional
	public void timingPay4OwnFeeUsers() {
		// 找出欠费用户列表
		List<User> userList = userService.findOweFeeUsers();
		for (User user : userList) {
			Lock lock = getFileLock(user);
			// 若可以成功获取文件锁
			if (lock.obtain() == true) {
				try{
					pay4OwnFeeUser(user);
				}catch (Exception e){
					logger.error(e.getMessage(), e);
				}finally{
					// 释放文件锁
					lock.unlock();
				}
			}
		}

	}

	@Transactional(propagation = Propagation.REQUIRES_NEW)
	public void pay4OwnFeeUser(User user) {
		logger.info(ILogMessages.PAY4OWE_FEE_USER_START, user.getUsername());
		String userId = user.getId();
		// 先支付用户没有支付清的账单
		List<Bill> billList = billService.findPayableBill(userId);
		Account account = accountService.findByUser(userId);
		for (Bill bill : billList) {
			List<BillItem> billItems = billItemService.findNeedPaidItems(bill
					.getId());
			for (BillItem billItem : billItems) {
				chargeOffService.chargeOff4BillItem(billItem, account, bill);
			}
			billService.update(bill);
		}

		// 支付欠费的预付款帐目
		List<AccountItem> prePaidItems = acctItemService
				.findOwningPrepaidItems(userId);
		for (AccountItem acctItem : prePaidItems) {
			acctItem = chargeOffService.chargeOff4AcctItem(acctItem, account);
		}
		logger.info(ILogMessages.PAY4OWE_FEE_USER_END, user.getUsername());
	}

	@Override
	@Transactional
	public String pay4Order(String orderId) throws AAAException {
		String result = null;
//		Lock lock = getFileLock(orderId);
//		try {
//			// 若可以成功获取文件锁
//			if (lock.obtain() == true) {
				Order order = orderService.findOne(orderId);
				if (order != null) {
					result = pay4Order(order);
				} else {
					throw new AAAException(IAAAErrorCode.ERROR_ORDER_NOT_EXIST);
				}

//			} else {
//				throw new AAAException(IAAAErrorCode.ERROR_PAY4ORDER_LOCK);
//			}
//		} finally {
//			// 释放文件锁
//			lock.unlock();
//		}
		return result;
	}

	private String pay4Order(Order order) throws AAAException {
		User user = order.getUser();
		Account account = accountService.findByUser(user.getId());
		if (account.getStatus() == Account.Status.FREEZE) {
			throw new AAAException(IAAAErrorCode.ERROR_ACCOUNT_FREEZE);
		}
		PayReceipt _payReceipt = payReceiptService.findByOrder(order.getId());
		if (_payReceipt != null) {
			throw new AAAException(IAAAErrorCode.ERROR_ORDER_HAS_PAID);
		}
		Product product = order.getProduct();
		double payableAmount = order.getPreFee();
		double prePaidAmount = 0.00;
		AccountBook spAcctBook = acctBookService.findSpecialAcctBook(account
				.getId());
//		Date date = DateUtils.today();
		List<PayReceiptDetail> payReceiptDetails = new ArrayList<PayReceiptDetail>();
//		// 先从可以使用的赠送类型的专款专用进行预扣费
//		List<SpecialAccountBookItem> spAcctBookItems = spAcctBookItemService
//				.findEffectGivenItemBySource(spAcctBook.getId(),
//						SpecialAccountBookItem.Source.PRODUCT, product.getId(),
//						date);
//		if (spAcctBookItems != null && spAcctBookItems.size() > 0) {
//			for (SpecialAccountBookItem spAcctBookItem : spAcctBookItems) {
//				// 如果已经没有需要支付的费用，则跳出循环
//				if (payableAmount == 0) {
//					break;
//				}
//				double balance = spAcctBookItem.getBalance();
//				double freezeBalance = spAcctBookItem.getFreezeBalance();
//				if (balance > 0) {
//					// 获取可以预扣的金额
//					double prePaid = (balance >= payableAmount ? payableAmount
//							: balance);
//					// 更新需支付的金额
//					payableAmount -= prePaid;
//					// 更新已预扣的金额
//					prePaidAmount += prePaid;
//					freezeBalance += prePaid;
//					// 更新专款专用项的可用余额和冻结金额
//					spAcctBookItemService.updateBalance(spAcctBookItem.getId(),
//							balance - prePaid, freezeBalance);
//					// 创建扣款凭证明细
//					payReceiptDetails.add(createPayReceiptDetail(prePaid,
//							spAcctBook, spAcctBookItem));
//				}
//			}
//		}

		int orderType = order.getType();
		if(orderType == Order.TYPE.OFFICIAL || orderType == Order.TYPE.TOOFFICIAL){
			// 判断若没有预扣款完成，使用现金账本预扣款
			if (payableAmount > 0) {
				AccountBook cashAcctBook = acctBookService.findCashAcctBook(account
						.getId());
				// 现金账本余额
				double balance = cashAcctBook.getBalance();
				double prePaid = 0.00;
				// 如果余额大于等于需付金额,否则抛出异常
				if (balance >= payableAmount) {
					prePaid = payableAmount;
					// 更新账本余额
					cashAcctBook.setBalance(balance - prePaid);
					prePaidAmount += prePaid;
					// 创建现金预付款类型的产品专款专用账本
					SpecialAccountBookItem spAcctBookItem =saveOrUpdateSpItem(spAcctBook,
							SpecialAccountBookItem.Source.PRODUCT, product.getId(),
							SpecialAccountBookItem.Type.PREPAID, prePaid, product.getName());
					//创建专款专用账本的明细
					SpAcctBookItemReceipt spReceipt = new SpAcctBookItemReceipt();
					spReceipt.setAmount(prePaid);
					spReceipt.setEffDate(order.getStartDate());
					spReceipt.setExpDate(order.getEndDate());
					spReceipt.setOrder(order);
					spReceipt.setSpAcctBookItem(spAcctBookItem);
					spReceipt.setCreatedTime(DateUtils.getCurrentTimestamp(DateUtils.YYYY_MM_DD_HH_MM_SS_PATTERN));
					spAcctBookItemReceiptService.save(spReceipt);
					// 创建支付凭证明细
					payReceiptDetails.add(createPayReceiptDetail(prePaid,
							cashAcctBook, null));
					acctBookService.update(cashAcctBook);
					spAcctBook.setBalance(spAcctBook.getBalance() + prePaid);
					acctBookService.update(spAcctBook);
					
					StringBuffer memoBuff = new StringBuffer();
					memoBuff.append("为产品[");
					memoBuff.append(product.getName());
					memoBuff.append("]订购预付款,相关订单编号：");
					memoBuff.append(order.getSn());
//					memoBuff.append(", 转入到专款专用账本");
					
					//记录现金账本的收支明细
					createReceiptDisburseStatement(account, cashAcctBook, cashAcctBook.getBalance(), prePaid, ReceiptDisburseStatement.Type.DISBURSE, memoBuff.toString(), memoBuff.toString());
					
					StringBuffer memo1Buff = new StringBuffer();
					memo1Buff.append("现金账本转入，为产品[");
					memo1Buff.append(product.getName());
					memo1Buff.append("]的预付款,相关订单编号：");
					memo1Buff.append(order.getSn());
					//记录专款专用账本的收入明细
					createReceiptDisburseStatement(account, spAcctBook, spAcctBook.getBalance(), prePaid, ReceiptDisburseStatement.Type.RECEIPT, memo1Buff.toString(), memo1Buff.toString());
				} else {
					throw new BalanceNotEnoughException(payableAmount-balance);
				}
			}
		} else if(orderType == Order.TYPE.RENEWALS){
			ProductInstance prodInst = order.getProductInstance();
			//从现金账本扣款到现金预付款类型的产品实例专款专用账本
			if (payableAmount > 0) {
				AccountBook cashAcctBook = acctBookService.findCashAcctBook(account
						.getId());
				// 现金账本余额
				double balance = cashAcctBook.getBalance();
				double prePaid = 0.00;
				// 如果余额大于等于需付金额,否则抛出异常
				if (balance >= payableAmount) {
					prePaid = payableAmount;
					// 更新账本余额
					cashAcctBook.setBalance(balance - prePaid);
					prePaidAmount += prePaid;
					// 创建现金预付款类型的产品实例专款专用账本
					SpecialAccountBookItem spAcctBookItem = saveOrUpdateSpItem(spAcctBook,
							SpecialAccountBookItem.Source.PRODUCT_INSTANCE, prodInst.getId(),
							SpecialAccountBookItem.Type.PREPAID, prePaid, prodInst.getProduct().getName());
					//创建专款专用账本的明细
					SpAcctBookItemReceipt spReceipt = new SpAcctBookItemReceipt();
					spReceipt.setAmount(prePaid);
					spReceipt.setEffDate(order.getStartDate());
					spReceipt.setExpDate(order.getEndDate());
					spReceipt.setOrder(order);
					spReceipt.setSpAcctBookItem(spAcctBookItem);
					spReceipt.setCreatedTime(DateUtils.getCurrentTimestamp(DateUtils.YYYY_MM_DD_HH_MM_SS_PATTERN));
					spAcctBookItemReceiptService.save(spReceipt);
					// 创建支付凭证明细
					payReceiptDetails.add(createPayReceiptDetail(prePaid,
							cashAcctBook, null));
					acctBookService.update(cashAcctBook);
					spAcctBook.setBalance(spAcctBook.getBalance() + prePaid);
					acctBookService.update(spAcctBook);
					
					StringBuffer memoBuff = new StringBuffer();
					memoBuff.append("为已办业务[");
					memoBuff.append(prodInst.getProduct().getName());
					memoBuff.append("]续费进行预付款,相关订单编号为：");
					memoBuff.append(order.getSn());
					memoBuff.append(",业务编号为：");
					memoBuff.append(prodInst.getSn());
//					memoBuff.append(", 转入到专款专用账本");
					
					//记录现金账本的收支明细
					createReceiptDisburseStatement(account, cashAcctBook, cashAcctBook.getBalance(), prePaid, ReceiptDisburseStatement.Type.DISBURSE, memoBuff.toString(), memoBuff.toString());
					
					StringBuffer memo1Buff = new StringBuffer();
					memo1Buff.append("现金账本转入，为已办业务[");
					memo1Buff.append(prodInst.getProduct().getName());
					memo1Buff.append("]续费进行预付款,相关订单编号为：");
					memo1Buff.append(order.getSn());
					memo1Buff.append(",业务编号为：");
					memo1Buff.append(prodInst.getSn());
					//记录专款专用账本的收入明细
					createReceiptDisburseStatement(account, spAcctBook, spAcctBook.getBalance(), prePaid, ReceiptDisburseStatement.Type.RECEIPT, memo1Buff.toString(), memo1Buff.toString());
				} else {
					throw new BalanceNotEnoughException(payableAmount-balance);
				}
			}
		}
		// 更改订单状态
		orderService.markPaid(order.getId());
		// 保存支付凭证
		PayReceipt payReceipt = new PayReceipt();
		payReceipt.setAmount(order.getPreFee());
		payReceipt.setOrder(order);
		for (PayReceiptDetail detail : payReceiptDetails) {
			payReceipt.addPayReceiptDetail(detail);
		}
		payReceipt.setCreatedTime(DateUtils.getCurrentTimestamp());
		payReceipt = payReceiptService.save(payReceipt);
		return payReceipt.getId();
	}

	private PayReceiptDetail createPayReceiptDetail(double amount,
			AccountBook acctBook, SpecialAccountBookItem spAcctBookItem) {
		PayReceiptDetail detail = new PayReceiptDetail();
		detail.setAmount(amount);
		detail.setAcctBook(acctBook);
		detail.setSpAcctBookItem(spAcctBookItem);
		detail.setCreatedTime(DateUtils.getCurrentTimestamp());
		return detail;
	}

	@Override
	@Transactional(rollbackFor = { Exception.class })
	public void pay4Bill(String billId){
		Bill bill = billService.findOne(billId);
		User user = bill.getUser();
		Account account = accountService.findByUser(user.getId());
		List<BillItem> billItems = billItemService.findNeedPaidItems(billId);
		for (BillItem billItem : billItems) {
			chargeOffService.chargeOff4BillItem(billItem, account, bill);
		}
		billService.update(bill);
	}
	
	private ReceiptDisburseStatement createReceiptDisburseStatement(Account account, AccountBook acctBook, double balance, double amount, String type, String memo, String memo1 ){
		ReceiptDisburseStatement receiptDisburseStatement = new ReceiptDisburseStatement();
		receiptDisburseStatement.setAccount(account);
		receiptDisburseStatement.setAccountBook(acctBook);
		receiptDisburseStatement.setBalance(balance);
		receiptDisburseStatement.setType(type);
		if(ReceiptDisburseStatement.Type.RECEIPT.equalsIgnoreCase(type)){
			receiptDisburseStatement.setReceipt(amount);
		}else{
			receiptDisburseStatement.setDisburse(amount);
		}
		receiptDisburseStatement.setCreateby("系统");
		receiptDisburseStatement.setCreateTime(DateUtils.getCurrentTimestamp());
		receiptDisburseStatement.setMemo(memo);
		receiptDisburseStatement.setMemo1(memo1);
		return receiptDisburseStatementService.save(receiptDisburseStatement);
	} 
}
